﻿using Sustainalytics.BussinesLogic.Insights;
using Sustainalytics.Claims;
using Sustainalytics.Entities;
using Sustainalytics.Insights.BusinessLogic;
using Sustainalytics.Insights.Service.Dto;
using Sustainalytics.Insights.Service.Utility;
using Sustainalytics.Replication.Publisher;
using Sustainalytics.Utils;
using Sustainalytics.Utils.Serilog;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Web.Http;
using System.Web.Http.Cors;
using WebApi.OutputCache.V2;

namespace Sustainalytics.Insights.Service
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    //[AutoInvalidateCacheOutput]
    [RoutePrefix("api/v1/insights")]
    [LogControllerFilter(true, true, false)]
	public class InsightsController : ApiController, IInsightController
	{
        private readonly IInsightBussinesLogic _blLayer;
        private readonly string _baseUrl;
        private readonly IClaimsChecker _claimsChecker;
        private ServiceBusPublisher _serviceBusPublisher { get; set; }

        public InsightsController(IInsightBussinesLogic blLayer, ServiceBusPublisher servicebuspublisher, IClaimsChecker claimCk)
        {
            _baseUrl = ConfigurationManager.AppSettings["ClientPlatformUrl"];
            _blLayer = blLayer;
            _claimsChecker = claimCk;
            _serviceBusPublisher = servicebuspublisher;
        }

        //[CacheOutput(ServerTimeSpan = 100, ClientTimeSpan = 0, NoCache = true, MustRevalidate = true, Private = true)]
        [Route("{id}")]
        public HttpResponseMessage Get(Guid id, bool includeAllFields = false, bool fromAdmin = false)
		{
            HttpResponseMessage response;
            try
            {
                if (!fromAdmin)
                {
                    if (!_claimsChecker.ValidateClaim(this))
                        throw new DashboardClaimNullException();
                }
                else
                {
                    if (!_claimsChecker.ValidateAdminClaim(this))
                        throw new AdminClaimNullException();
                }

                var insight = _blLayer.Read(id);
                if (insight != null)
                {
                    var result = InsightDto.FromInsight(insight, _baseUrl, includeAllFields);
                    response = Request.CreateResponse(HttpStatusCode.OK, result);
                }
                else
                {
                    response = Request.CreateResponse(HttpStatusCode.NotFound);
                }
		    }
            catch (DashboardClaimNullException)
            {
                return this.Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = 3002 });
            }
            catch (AdminClaimNullException)
            {
                return this.Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = 3003 });
            }
		    catch (Exception ex)
		    {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
		    }            
            return response;
		}

       // [CacheOutput(ServerTimeSpan = 100, ClientTimeSpan = 0, NoCache = true, MustRevalidate = true, Private = true)]
        public HttpResponseMessage Get([FromUri] ListOptions options, [FromUri] InsightFilterDto filter, bool fromAdmin = false)
        {
            options = options ?? ListOptions.Default;

            HttpResponseMessage response;
            try
            {
                if (!fromAdmin)
                {
                    if (!_claimsChecker.ValidateClaim(this))
                        throw new DashboardClaimNullException();
                }
                else
                {
                    if (!_claimsChecker.ValidateAdminClaim(this))
                        throw new AdminClaimNullException();
                }

                if (options.GetCount)
                {
                    if (filter == null)
                        filter = new InsightFilterDto();

                    var count = _blLayer.ReadCount(filter.BrowserLocalDateTime, filter.TitlePart, filter.Status,
                                                        filter.InsightTopicId, filter.InsightSectorId, filter.NoLongerThan);

                    response = Request.CreateResponse(HttpStatusCode.OK, new { count = count });
                }
                else
                {

                    var insightList = _blLayer.Read(filter.BrowserLocalDateTime, filter.TitlePart, filter.Status,
                                                    filter.InsightTopicId, filter.InsightSectorId, filter.NoLongerThan,
                                                    options.Skip, options.Take, options.OrderBy != null ? options.OrderBy.First() : null, options.OrderAscending);

                    var result = insightList.Select(e => InsightDto.FromInsight(e, _baseUrl, options.IncludeAllFields));
                    response = Request.CreateResponse(HttpStatusCode.OK, result);
                }
            }
            catch (DashboardClaimNullException)
            {
                return this.Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = 3002 });
            }
            catch (AdminClaimNullException)
            {
                return this.Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = 3003 });
            }
            catch (Exception ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
            }      
            return response;
        }

        [HttpPost]
        public virtual HttpResponseMessage Post([FromBody] InsightDto insight)
        {
            //todo  petre clarify what we are doing with user id 
            try
            {
                if (!_claimsChecker.ValidateAdminClaim(this))
                    throw new AdminClaimNullException();

                Guid userId = new Guid(ClaimsPrincipal.Current.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier).SingleOrDefault().Value);
                var userName = ClaimsPrincipal.Current.Claims.Where(s => s.Type == ClaimTypes.GivenName)
                    .Select(s => s.Value)
                    .SingleOrDefault() + " " + ClaimsPrincipal.Current.Claims.Where(s => s.Type == ClaimTypes.Surname)
                        .Select(s => s.Value)
                        .SingleOrDefault();

                _blLayer.Create(insight.ToInsight(userId, userName));
                return Request.CreateResponse(HttpStatusCode.Created);
            }
            catch (AdminClaimNullException)
            {
                return this.Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = 3003 });
            }
            catch (ArgumentOutOfRangeException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.Message);
            }
            catch (Exception ex)
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
            }
        }

        [HttpPut]
        public virtual HttpResponseMessage Put([FromBody] InsightDto insight)
        {
            try
            {
                if (!_claimsChecker.ValidateAdminClaim(this))
                    throw new AdminClaimNullException();

                _blLayer.Update(insight.ToInsight(Guid.Empty, null));
                return Request.CreateResponse(HttpStatusCode.NoContent);
            }
            catch (AdminClaimNullException)
            {
                return this.Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = 3003 });
            }
            catch (ArgumentOutOfRangeException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.Message);
            }
            catch (Exception ex)
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
            }
        }

        [AcceptVerbs("Patch")]
        [InvalidateCacheOutput("Get")]
        public virtual HttpResponseMessage Patch([FromBody] List<InsightPatchOpDto> list)
        {
            try
            {
                if (!_claimsChecker.ValidateAdminClaim(this))
                    throw new AdminClaimNullException();

                var toDelete = list.Where(x => x.Verb == "DELETE").Select(x => x.ResourceId).ToList();
                var toUpdate = list.Where(x => x.Verb == "PATCH").Select(x => new Tuple<Guid, InsightState>(x.ResourceId, x.NewState)).ToList();
                
                var resultDelete = (toDelete.Count >0)? _blLayer.Delete(toDelete): true;
                var resultUpdate = (toUpdate.Count > 0) ? _blLayer.Update(toUpdate) : true;                

                if (resultDelete && resultUpdate)
                    return Request.CreateResponse(HttpStatusCode.OK);

                return Request.CreateResponse(HttpStatusCode.InternalServerError);
            }
            catch (AdminClaimNullException)
            {
                return this.Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = 3003 });
            }
            catch (Exception ex)
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
            }            
        }

        [HttpDelete]
        [Route("{id}")]
        public virtual HttpResponseMessage Delete(Guid id)
		{
            try
            {
                if (!_claimsChecker.ValidateAdminClaim(this))
                    throw new AdminClaimNullException();

                _blLayer.Delete(new List<Guid>(){id});
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (AdminClaimNullException)
            {
                return this.Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = 3003 });
            }
            catch (Exception ex)
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
            }
		}
	}
}

